/**
 *  translate-up-down.js  用于实现移动端页面上下滑动 (2015-06-24)
 *  修复存在的一些问题 (2015-11-13)
 *  添加监听事件 (2015-11-16)
 */
(function(win) {
    /** 
     *@method zScroll
     *@param {object} options 传入参数
     *@param {Element} options.wrap 外层元素
     */
    function zScroll(options) {
        // private
        var root = win.document,
            /**
             *存放默认设置
             *@param {object || string} wrap 是滑动层的外层元素 可以是页面元素也可以是className 、 id
             *@param {Number} threshold  可脱离wrap的最大距离
             *@param {Number} damping  阻尼
             */
            _this = this,
            settings = {
                wrap: null,
                threshold: 0,
                damping: 0.33
            },

            util = {
                //返回对象类型
                type: function(obj) {
                    var str = ({}).toString.call(obj),
                        ret = str.match(/\s+(.*)\]$/);
                    return ret && ret[1] && ret[1].toLowerCase()
                },
                //拷贝函数
                extend: function() {
                    var dest, source, deep = false,
                        i, t;
                    if (typeof arguments[0] == 'boolean') {
                        deep = arguments[0];
                        dest = arguments[1];
                        source = arguments[2];
                    } else {
                        dest = arguments[0];
                        source = arguments[1];
                    }

                    if (!dest || this.type(dest) != 'object') {
                        dest = {};
                    }
                    if (!source) {
                        return dest;
                    }

                    for (i in source) {
                        if (source[i]) {
                            t = this.type(source[i]);
                            if ((t == 'object' || t == 'array') && deep) {
                                if (t == 'object') {
                                    dest[i] = this.type(dest[i]) == "object" ? dest[i] : {};
                                } else {
                                    dest[i] = this.type(dest[i]) == "array" ? dest[i] : [];
                                }
                                this.extend(deep, dest[i], source[i]);
                            } else {
                                dest[i] = source[i];
                            }
                        }
                    }
                    return dest;
                },

                hasTouch: !!('ontouchstart' in window || window.DocumentTouch && document instanceof window.DocumentTouch),

                eventType: function(type) {
                    var hasTouch = this.hasTouch,
                        types = {
                            start: hasTouch ? 'touchstart' : 'mousedown',
                            move: hasTouch ? 'touchmove' : 'mousemove',
                            end: hasTouch ? 'touchend' : 'mouseup'
                        };
                    return types[type];
                },
                //返回私有前缀
                vendor: function() {
                    var style = 'transition',
                        tem, i,
                        prefix = 'webkit Moz O ms'.split(' ');
                    style = style.replace(/^./, function(w) {
                        return w.toUpperCase()
                    });
                    tem = root.createElement("div");
                    for (i in prefix) {
                        if (tem.style[prefix[i] + style] !== undefined) {
                            return prefix[i]
                        }
                    }
                    tem = null
                },
                page: function(e, axis) {
                    if (e.type == "touchend") {
                        return e.changedTouches[0]['page' + axis];
                    }
                    return (this.hasTouch && e.touches.length && e.touches[0]) ? e.touches[0]['page' + axis] : e['page' + axis];
                },
                getTranslate: function(ele) {
                    var matrix = (win.getComputedStyle(ele)[cache.vendor + 'Transform']).split(',');
                    return parseInt(matrix[5]) || 0;
                },
                //惯性运动
                momentum: function(current, start, maxtop, maxbottom, time, deceleration) {
                    var v = (current - start) / time,
                        dis = 0,
                        duration, dest;
                    deceleration = deceleration || 0.0006;
                    dis = (v * v) / (2 * deceleration) * (current - start > 0 ? 1 : -1);
                    dest = cache.translate + dis;
                    duration = Math.abs(v / deceleration);
                    if (dest > maxtop) {
                        dest = maxtop;
                        duration = Math.abs((dest - cache.translate) / v);
                    } else if (dest < maxbottom) {
                        dest = maxbottom;
                        duration = Math.abs((dest - cache.translate) / v);
                    }
                    return {
                        'dest': dest,
                        'duration': duration
                    };
                },
                ua: {
                    UA: win.navigator.userAgent,
                    isIos: function() {
                        return /iphone|ipad|ipod/i.test(this.UA)
                    },
                    isAndroid: function() {
                        return /android/i.test(this.UA)
                    }
                }
            },

            //缓存
            cache = {},

            //事件队列
            eventList = {},

            //事件管理
            action = {
                handler: {
                    //实现这个接口，用来分发事件
                    handleEvent: function(e) {
                        switch (e.type) {
                            case util.eventType('start'):
                                this.startHandle(e);
                                break;
                            case util.eventType('move'):
                                this.moveHandle(e);
                                break;
                            case util.eventType('end'):
                                this.endHandle(e);
                                break;
                            case 'transitionend':
                            case 'webkitTransitionEnd':
                            case 'oTransitionEnd':
                            case 'MSTransitionEnd':
                                this.transitionendHandle(e);
                        }
                    },
                    startHandle: function(e) {
                        cache.startX = util.page(e, 'X');
                        cache.startY = util.page(e, 'Y');
                        cache.lastX = cache.startX;
                        cache.lastY = cache.startY;
                        cache.isStart = true;
                        cache.startTime = (new Date).valueOf();
                        cache.translate = util.getTranslate(settings.ele);
                        //目前滑动值
                        if (cache.status.status == 'move') {
                            cache.translate = util.getTranslate(settings.ele);
                            action.move.easeTo(cache.translate, 0, '');
                            cache.status.status = 'end';
                        } else {
                            action.execEvent('scrollStart');
                        }
                        cache.status.status = 'start';
                    },
                    moveHandle: function(e) {
                        var dis,
                            target,
                            y, t,
                            canMove = !(cache.translate > cache.top || cache.translate < cache.bottom);
                        if (cache.isStart && canMove) {
                            cache.isAndroid && e.preventDefault();
                            y = util.page(e, 'Y');
                            t = (new Date).valueOf();
                            cache.status.status = 'move';
                            cache.direction = y - cache.lastY < 0 ? 'down' : 'up';
                            if (cache.translate > 0 || cache.translate < -settings.ele.offsetHeight + cache.screen.h) {
                                cache.translate = cache.translate + (y - cache.lastY) * settings.damping;
                            } else {
                                cache.translate = cache.translate + (y - cache.lastY);
                            }
                            //防止拉得太过
                            if (cache.translate > cache.top) {
                                cache.translate = cache.top;
                            } else if (cache.translate < cache.bottom) {
                                cache.translate = cache.bottom
                            }
                            action.move.easeTo(cache.translate);
                            cache.lastX = util.page(e, 'X');
                            cache.lastY = y;
                            action.execEvent('scrollMove');
                            //replace, 一直按住，突然滑动
                            if (t - cache.startTime > 300) {
                                cache.startX = cache.lastX;
                                cache.startY = cache.lastY;
                                cache.startTime = t;
                            }
                        }
                    },
                    endHandle: function(e) {
                        var dest, y, t, o;
                        cache.lastX = util.page(e, 'X');
                        cache.lastY = util.page(e, 'Y');
                        cache.endX = cache.lastX;
                        cache.endY = cache.lastY;
                        cache.isStart = false;
                        cache.endTime = (new Date).valueOf();

                        function resetPostion() {
                            if (cache.translate > 0) {
                                cache.translate = 0;
                                action.move.easeTo(0, 100);
                            } else if (cache.translate < -settings.ele.offsetHeight + cache.screen.h) {
                                cache.translate = -settings.ele.offsetHeight + cache.screen.h;
                                action.move.easeTo(-settings.ele.offsetHeight + cache.screen.h, 100);
                            } else {
                                action.execEvent('scrollEnd');
                                cache.status.status = 'end';
                            }
                        }
                        //swipe
                        //增加速度判断,去掉时间判断
                        if (Math.abs(cache.endY - cache.startY) > 10 && Math.abs(cache.endY - cache.startY) / (cache.endTime - cache.startTime) > 0.65) {
                            o = util.momentum(cache.endY, cache.startY, cache.top, cache.bottom, cache.endTime - cache.startTime);
                            dest = o.dest;
                            t = o.duration;
                            cache.translate = dest;
                            cache.status.status = 'move';
                            action.move.easeTo(dest, t, 'cubic-bezier(0.1, 0.57, 0.1, 1)');
                            //页面在顶部或是底部时swipe
                            if (!t) {
                                resetPostion();
                            }
                        } else {
                            resetPostion();
                        }
                    },
                    transitionendHandle: function(e) {
                        if (cache.status && cache.status.status == 'move') {
                            if (cache.translate > 0) {
                                cache.translate = 0;
                                action.move.easeTo(0, 100);
                            } else if (cache.translate < -settings.ele.offsetHeight + cache.screen.h) {
                                cache.translate = -settings.ele.offsetHeight + cache.screen.h;
                                action.move.easeTo(-settings.ele.offsetHeight + cache.screen.h, 100);
                            }
                            action.execEvent('scrollEnd');
                            cache.status.status = 'end';
                        }
                    }
                },
                //运动函数
                move: {
                    easeTo: function(w, time, effect) {
                        time = time || 0;
                        effect = effect || '';
                        cache.translate = w;
                        settings.ele.style[cache.vendor + 'Transition'] = time + 'ms ' + effect;
                        settings.ele.style[cache.vendor + 'Transform'] = 'translate3d(0,' + w + 'px,0)';
                    }
                },
                execEvent: function(fn) {
                    eventList[fn] && eventList[fn].call(_this)
                },
                bind: function() {
                    settings.ele.addEventListener(util.eventType('start'), this.handler);
                    settings.ele.addEventListener(util.eventType('move'), this.handler);
                    settings.ele.addEventListener(util.eventType('end'), this.handler);
                    settings.ele.addEventListener(cache.vendor + 'TransitionEnd', this.handler);
                },
                unbind: function() {
                    settings.ele.addEventListener(util.eventType('start'), this.handler);
                    settings.ele.addEventListener(util.eventType('move'), this.handler);
                    settings.ele.addEventListener(util.eventType('end'), this.handler);
                }
            },
            init = function() {
                if (options.wrap) {
                    util.extend(settings, options);
                    settings.wrap = typeof settings.wrap == 'string' ? root.querySelector(settings.wrap) : settings.wrap;
                    settings.ele = settings.wrap.firstElementChild || root.createElement('div');
                    //将需要的信息交给cache
                    cache.vendor = util.vendor();
                    cache.screen = {
                        w: settings.wrap.offsetWidth,
                        h: settings.wrap.offsetHeight
                    };
                    cache.status = cache.status || {};
                    cache.isIos = util.ua.isIos();
                    cache.isAndroid = util.ua.isAndroid();
                    //可以脱离外层元素的最大距离
                    cache.threshold = settings.threshold || Math.min(cache.screen.w, cache.screen.h) / 5;
                    //滑动的区域
                    cache.top = cache.threshold;
                    cache.bottom = -settings.ele.offsetHeight + cache.screen.h - cache.threshold;
                    //event
                    eventList.scrollStart = settings.onscrollStart;
                    eventList.scrollMove = settings.onscrollMove;
                    eventList.scrollEnd = settings.onscrollEnd;
                    //bind
                    action.bind();
                }
            };

        init();
        // public
        this.getCache = function() {
            return cache;
        }
        this.getSettings = function() {
            return settings;
        }
        this.reset = function() {
            init();
        }
    }
    win.zScroll = zScroll;

})(window);
